package game.io;
import java.util.*;
import java.io.*;
import room.*;
import items.*;
import commands.*;
import game.*;

public class TagIO
{
	public TagIO(){}

	//XMLout takes a game object, iterates through its room collections
	//iterates through each room's states and items, each state's verbs,
	//each verb's commands, and then back up to each item's state.
	//Whew. It's kind of like a depth first search.
	//Each piece of information is stored in contextually relevant, human
	//readable tags in the File object passed.
	//Complexity: O[rooms*(roomstates*verbs*commands+items*itemstates)]
	public void XMLout(Game game, File savefile)
	{
		//Create an output stream to store the file
		FileOutputStream opf;
		PrintStream output;
		//This is the set of iterators to cull information
		//(iteratively) from the game object
		Iterator<Room> i;
		Iterator<RoomState> j;
		Iterator<String> k;
		Iterator<Command> l;
		
//		Iterator<Item> n;
//		Iterator<ItemState> m;
		

		try {
			//Instance up some file output
			opf = new FileOutputStream(savefile);
			output = new PrintStream(opf);
			
			//When we load the game we'll need to know how many rooms there are
			output.println("<game rooms=\""+game.getRooms().size()+"\">");
			//And also the room you're supposed to start in
			output.println("\t<initialroom id=\""+game.getCurrentRoom().getId()+"\" />");


			//The iterator ensures we explore every room
			for (i = game.getRooms().iterator (); i.hasNext (); ) {
				
				//We make a room i to store all its information
				Room thisroom = i.next();
				//And we give it tags to remember its id and name
				output.println("\t<room id=\""+thisroom.getId()+"\" name=\""+thisroom.getName()+"\">");
				output.println("\t\t<initialstate id=\""+thisroom.getCurrentState().getId()+"\" />");


				//Each room holds a collection of room states
				//We need to explore each of these as well
				for (j = thisroom.getStates().iterator(); j.hasNext(); ) {
					
					//Like before, we create a roomstate j that holds the roomstate we need to look through
					RoomState thisroomstate = j.next();
					//But here the important attributes are the state's id and description
					output.println("\t\t<state id=\""+thisroomstate.getId()+"\">");
					output.println("\t\t\t<statedescription>"+thisroomstate.getDescription().trim()+"</statedescription>");


					//Each state has a collection of verbs so that one may interact with the environment
					for (k = thisroomstate.getVerbs().iterator(); k.hasNext(); ) {
						//And we look through one verb at a time
						String thisverb = k.next();
						//...The pertinent information being the string one enters to execute the verb
						output.println("\t\t\t<verb name=\""+thisverb+"\">");


						//Each verb, in turn has a collection of commands
						for (l = thisroomstate.getVerbCommands(thisverb).iterator(); l.hasNext(); ) {
							
							//So we look at one command
							Command thiscommand = l.next();
							
							//The commands are the hard-coded fundamental structures of action
							//Thus we have a finite number of possibilities:
							//The command might be a Say:
							if (thiscommand instanceof commands.Say) {
								//In which case we cast it...
								commands.Say temp = (commands.Say)thiscommand;
								//And a Say can be a Say description
								if (thiscommand instanceof commands.SayDescription) {
									//Where the "response" is the room description
									output.println("\t\t\t\t<saydescription />");
								} else {
									//Or a regular say, where the user specifies the string
									output.println("\t\t\t\t<say response=\""+temp.getResponse().trim()+"\" />");
								}
							}
							
							//Or it might be a ChangeState
							if (thiscommand instanceof commands.ChangeState) {
								commands.ChangeState temp = (commands.ChangeState)thiscommand;
								//So we cast it and save the room which it changes and the state to which the room changes
								output.println("\t\t\t\t<changestate roomid=\""+temp.getRoom().getId()+"\" stateid=\""+temp.getStateID()+"\" />");
							}
							
							//And finally, it might be a Move
							if (thiscommand instanceof commands.Move) {
								commands.Move temp = (commands.Move)thiscommand;
								//A Move is a link from this room to another. We're in this room so we need to store the other.
								output.println("\t\t\t\t<move roomid=\""+temp.getRoom().getId()+"\" />");
							}
						}
						
						//Ending tags for each verb
						output.println("\t\t\t</verb>");
					}
					
					//Ending tags for each state
					output.println("\t\t</state>");
				}

//				Do not open 'til items are done! =)
//
//				for (n = thisroom.getItems().iterator(); n.hasNext(); ) {
//					Item thisitem = n.next();
//					output.println("\t\t<item id=\""+thisitem.getId()+"\" name=\""+thisitem.getName()+"\">");
//					
//					for (m = thisitem.getStates().iterator(); m.hasNext(); ) {
//						ItemState thisitemstate = m.next();
//						output.println("\t\t\t<itemstate id=\""+thisitemstate.getId()+"\" desc=\""+thisitemstate.getDescription()+"\" />");
//					}			
//					output.println("\t\t</item>");
//				}
				
				//Ending tags for each room
				output.println("\t</room>");
			}
			
			//Closing the tags and the file... and we're done here!
			output.println("</game>");
			output.close();
		}
		
		//If something explodes, we could hardly be more informative.
		catch (Exception e) {
			System.err.println ("Error writing to file.");
		}
	}

	//XMLin takes a File that is hopefully formatted as a TAG savegame.
	//It reads progressively (line-by-line) through the input and builds
	//a game object that holds the same information, but in a way that can
	//be played by the graphical editor or the command line compiler
	public Game XMLin(File savefile)
	{
		//Instance a new game and a new command. We'll create other objects as needed. 
		Game game = new Game();
		Commands thisverb = new Commands();
		
		//An input stream
		BufferedReader in;
		
		//And some counters and holders
		Integer i, j, k, l;
		Integer initroomid = 0, initstateid = 0;
		String commandstring="";
		String line="", iname="";
      
		try {
			//Instance the input
			in = new BufferedReader(new FileReader(savefile));
			
			//Read each line until we have read the closing tag
			while (line.compareTo("</game>") != 0)
			{
				//Read in the tags and information on the current line
				line = in.readLine().trim();
				
				//If it's a game tag, we have the number of rooms to get
				if (line.startsWith("<game"))
				{
					//So get the number in quotes
					i = line.indexOf("\"");
					j = line.indexOf("\"", ++i);
					k = Integer.valueOf(line.substring(i, j));
					
					//And create that many rooms in our game
					for (i=0; i<k; i++)
					{
						//The only problem to fix here is that if you have n rooms,
						//each room id must be an element of [0, n-1]
						game.createRoom().setId(i);
					}
				}
				
				//If it's an initialroom tag, we have the id of the starting room
				if (line.startsWith("<initialroom"))
				{
					//The number in quotes gets set in the initial room holder
					i = line.indexOf("\"");
					j = line.indexOf("\"", ++i);
					initroomid = Integer.valueOf(line.substring(i, j));				
				}
				
				//The room tag has an id and a name
				if (line.startsWith("<room"))
				{
					//Grab the id and set it, and the select that room so we can fill it
					i = line.indexOf("\"");
					j = line.indexOf("\"", ++i);
					k = Integer.valueOf(line.substring(i, j));
					game.setCurrentRoom(game.getRoom(k));

					//And now grab the name. It's important.
					i = line.indexOf("\"", ++j);
					j = line.indexOf("\"", ++i);
					game.getCurrentRoom().setName(line.substring(i, j));
					
					//Since we know we're reading room information, let's grab another line
					line = in.readLine().trim();
					//...and until we have all the information, we loop here
					while (line.compareTo("</room>") != 0)
					{
						//This tag gives us the initial room state
						if (line.startsWith("<initialstate"))
						{
							//So we get the number in quotes and put it in a placeholder
							i = line.indexOf("\"");
							j = line.indexOf("\"", ++i);
							initstateid = Integer.valueOf(line.substring(i, j));				
						}
						
						//Note: This is the "state" tag all on it's lonesome.
						//It has
						if (line.startsWith("<state "))
						{
							i = line.indexOf("\"");
							j = line.indexOf("\"", ++i);
							
							//We need to make a new state in the room to accomodate, and it's the one we're going to be building
							if (game.getCurrentRoom().getRoomState(Integer.valueOf(line.substring(i, j))) == null)
							{
								game.getCurrentRoom().setCurrentState(game.getCurrentRoom().createRoomState());
							}
							//And then we need to set its id from the tag information
							game.getCurrentRoom().getCurrentState().setId(Integer.valueOf(line.substring(i, j)));
						}
						
//						Not until items are done!
//						
//						if (line.startsWith("<item "))
//						{
//							i = line.indexOf("\"");
//							j = line.indexOf("\"", ++i);
//							k = Integer.valueOf(line.substring(i, j));
//							i = line.indexOf("\"", ++j);
//							j = line.indexOf("\"", ++i);
//							iname = line.substring(i, j);
//							
//							if (game.getCurrentRoom().getItem(Integer.valueOf(k)) == null)
//							{
//								Item item = game.getCurrentRoom().createItem());
//							}
//							item.setId(k);
//							item.setName(iname);
//							
//							line = in.readLine().trim();
//						
//							while (line.compareTo("</item>") != 0)
//							{
//								i = line.indexOf("\"");
//								j = line.indexOf("\"", ++i);
//								k = Integer.valueOf(line.substring(i, j));
//								i = line.indexOf("\"", ++j);
//								j = line.indexOf("\"", ++i);
//								isdesc = line.substring(i, j);
//								
//								Item item = new Item();
//								ItemState is = item.createItemState();
//								is.setId(k);
//								is.setDescription(isdesc);
//								
//								line = in.readLine().trim();								
//							}
//						}
						
						//If we get a state description...
						if (line.startsWith("<statedescription"))
						{
							//Then we take the string between the opening and closing tags
							i = line.indexOf(">");
							j = line.indexOf("<", ++i);
							//and make that the description of the room we're in
							game.getCurrentRoom().getCurrentState().setDescription(line.substring(i, j).trim());
						}
						
						//And now, if we see a verb
						if (line.startsWith("<verb"))
						{
							i = line.indexOf("\"");
							j = line.indexOf("\"", ++i);
							//Then we add it to our list of verbs, along with the string which executes it
							thisverb = game.getCurrentRoom().getCurrentState().addVerb(line.substring(i, j));
						}
						
						//Commands are the only things in self-closing tags.
						//Oh, except initialroom and initialstate. Ignore those.
						if (line.endsWith("/>") && !line.startsWith("<i"))
						{
							//The name of the command is the name of the tag
							//it occurs between the opening < and the first space
							i = line.indexOf(" ");
							commandstring = line.substring(1, i);
							
							//So, if it's a SayDescription
							if (commandstring.equalsIgnoreCase("saydescription"))
							{
								//We add a new SayDescription to the verb, of course!
								//The constructor is smart enough to know the room is already described
								thisverb.add(new SayDescription(game.getCurrentRoom().getCurrentState()));
							}
							
							//If it's just a plain old say
							if (commandstring.equalsIgnoreCase("say"))
							{
								i = line.indexOf("\"");
								j = line.indexOf("\"", ++i);
								//Then here we need to make a Say and associate it with the "response" string
								thisverb.add(new Say(line.substring(i, j)));
							}
							
							//Moves are easy
							if (commandstring.equalsIgnoreCase("move"))
							{
								i = line.indexOf("\"");
								j = line.indexOf("\"", ++i);
								k = Integer.valueOf(line.substring(i, j));
								//We just add a link to the room indexed by the "roomid" (k)
								thisverb.add(new Move(game, game.getRoom(k)));
							}
							
							//Change states are only a wee bit more complicated
							if (commandstring.equalsIgnoreCase("changestate"))
							{
								i = line.indexOf("\"");
								j = line.indexOf("\"", ++i);
								k = Integer.valueOf(line.substring(i, j));
								i = line.indexOf("\"", ++j);
								j = line.indexOf("\"", ++i);
								l = Integer.valueOf(line.substring(i, j));
								//k holds the "roomid," l holds the "stateid"
								//And our verb will change room k to state l!
								thisverb.add(new ChangeState(game.getRoom(k), l));
							}
						}
						
						//Read the next line in
						line = in.readLine().trim();
					}
					
					//Having read the whole room, we can now initialize its state
					game.getCurrentRoom().setCurrentState(initstateid);
				}
			}
			
			//We can now close our input stream, set the initial room, and send the fully assembled game object
			in.close();
			game.setCurrentRoom(game.getRoom(initroomid));
			return game;
		}
		
		//Once again, it doesn't get much more helpful than this!
		catch (Exception e) {
			System.err.println (e+"\nError reading from file.");
		}
		
		//If there's a problem, we ensure at least an empty game gets passed back
		return null;
	}
}